package com.czg.jsonschema2lombokpojo.rules;

import java.util.List;
import java.util.Set;

import com.czg.jsonschema2lombokpojo.Schema;
import com.czg.jsonschema2lombokpojo.util.Inflector;

import com.fasterxml.jackson.databind.JsonNode;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;

/**
 * 应用“type”：“array”schema规则。
 *
 * @author czg
 * @see <a
 *      href="http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.5">http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.5</a>
 * @see <a
 *      href="http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.15">http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.15</a>
 */
public class ArrayRule implements Rule<JPackage, JClass> {

    private final RuleFactory ruleFactory;

    protected ArrayRule(RuleFactory ruleFactory) {
        this.ruleFactory = ruleFactory;
    }

    /**
     * <p>用此则执行所需的代码生成步骤。</p>
     *
     * <p>当“数组”类型的构造出现在模式中时，这些会被映射到生成的 POJO 中的 Java 集合。如果数组被标记为具有“唯一元素”属性，则生成的 Java 类型为 {@link Set}，否则为 {@link List}。由“items”给出的模式将决定集合的泛型类型。</p>
     *
     * <p>如果“items”属性需要生成新的类型，那么类型名称将会是节点名称的单数形式（除非被 javaType 属性所覆盖）；例如：
     * <pre>
     *  "fooBars" : {"type":"array", "uniqueItems":"true", "items":{type:"object"}}
     *  ==&gt;
     *  {@code Set<FooBar> getFooBars(); }
     * </pre>
     *
     * @param nodeName
     *            具有“数组”类型属性的名称
     * @param node
     *            “类型”节点
     * @param parent
     *            父节点
     * @param jpackage
     *            “type”节点是将新生成的类型添加到其中的包的名称。
     * @return 与该数组规则相关联的 Java 类型，要么是 {@link Set}（集合），要么是 {@link List}（列表），且通过“items”类型进行了限定
     */
    @Override
    public JClass apply(String nodeName, JsonNode node, JsonNode parent, JPackage jpackage, Schema schema) {

        boolean uniqueItems = node.has("uniqueItems") && node.get("uniqueItems").asBoolean();
        boolean rootSchemaIsArray = !schema.isGenerated();

        JType itemType;
        if (node.has("items")) {
            String pathToItems;
            if (schema.getId() == null || schema.getId().getFragment() == null) {
                pathToItems = "#/items";
            } else {
                pathToItems = "#" + schema.getId().getFragment() + "/items";
            }
            Schema itemsSchema = ruleFactory.getSchemaStore().create(schema, pathToItems, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());
            if (itemsSchema.isGenerated()) {
                itemType = itemsSchema.getJavaType();
            } else {
                itemType = ruleFactory.getSchemaRule().apply(makeSingular(nodeName), node.get("items"), node, jpackage, itemsSchema);
                itemsSchema.setJavaTypeIfEmpty(itemType);
            }
        } else {
            itemType = jpackage.owner().ref(Object.class);
        }

        JClass arrayType;
        if (uniqueItems) {
            arrayType = jpackage.owner().ref(Set.class).narrow(itemType);
        } else {
            arrayType = jpackage.owner().ref(List.class).narrow(itemType);
        }

        if (rootSchemaIsArray) {
            schema.setJavaType(arrayType);
        }

        return arrayType;
    }

    private String makeSingular(String nodeName) {
        return Inflector.getInstance().singularize(nodeName);
    }

}
